#ifndef _HTTPCLIENTSESSION_HPP_
#define _HTTPCLIENTSESSION_HPP_

#include "jhdefs.hpp"
#include "types.hpp"
#include "net/HTTPSession.hpp"
#include "net/SocketAddress.hpp"
#include "net/StreamSocket.hpp"
#include "net/HTTPRequest.hpp"
#include "net/HTTPResponse.hpp"

NET_NAMESPACE_BEGIN

class CHTTPClientSession : public CHTTPSession
{
public:
	struct ProxyConfig
	{
		ProxyConfig() : port(HTTP_PORT)
		{
		}

		std::string  host;
		UInt16 port;
		std::string  username;
		std::string  password;
		std::string  nonProxyHosts;
	};

	CHTTPClientSession();
	explicit CHTTPClientSession(const CStreamSocket& socket);
	explicit CHTTPClientSession(const CSocketAddress& address);
	CHTTPClientSession(const std::string& host, UInt16 port = CHTTPSession::HTTP_PORT);
	CHTTPClientSession(const std::string& host, UInt16 port, const ProxyConfig& proxyConfig);
	virtual ~CHTTPClientSession();

	void setHost(const std::string& host);
	const std::string& getHost() const;
	void setPort(UInt16 port);
	UInt16 getPort() const;
	void setProxy(const std::string& host, UInt16 port = CHTTPSession::HTTP_PORT);
	void setProxyHost(const std::string& host);
	const std::string& getProxyHost() const;
	void setProxyPort(UInt16 port);
	UInt16 getProxyPort() const;
	void setProxyCredentials(const std::string& username, const std::string& password);
	void setProxyUsername(const std::string& username);
	const std::string& getProxyUsername() const;
	void setProxyPassword(const std::string& password);
	const std::string& getProxyPassword() const;
	void setProxyConfig(const ProxyConfig& config);
	const ProxyConfig& getProxyConfig() const;

	static void setGlobalProxyConfig(const ProxyConfig& config);
	static const ProxyConfig& getGlobalProxyConfig();

	void setKeepAliveTimeout(const CTimeSpan& timeout);
	const CTimeSpan& getKeepAliveTimeout() const;

	virtual std::ostream& sendRequest(CHTTPRequest& request);
	virtual std::istream& receiveResponse(CHTTPResponse& response);

	void reset();
	virtual bool secure() const;
	bool bypassProxy() const;

protected:
	enum
	{
		DEFAULT_KEEP_ALIVE_TIMEOUT = 8
	};

	void reconnect();
	int write(const char* buffer, std::streamsize length);

	virtual std::string proxyRequestPrefix() const;
	virtual bool mustReconnect() const;
	virtual void proxyAuthenticate(CHTTPRequest& request);
	void proxyAuthenticateImpl(CHTTPRequest& request);
	CStreamSocket proxyConnect();
	void proxyTunnel();

private:
	std::string					m_host;
	UInt16						m_port;
	ProxyConfig					m_proxyConfig;
	CTimeSpan					m_keepAliveTimeout;
	CTimeStamp					m_lastRequest;
	bool						m_reconnect;
	bool						m_mustReconnect;
	bool						m_expectResponseBody;
	TSharedPtr<std::ostream>	m_pRequestStream;
	TSharedPtr<std::istream>	m_pResponseStream;

	static ProxyConfig			m_globalProxyConfig;

	CHTTPClientSession(const CHTTPClientSession&);
	CHTTPClientSession& operator = (const CHTTPClientSession&);

	friend class CWebSocket;
};

NET_NAMESPACE_END

#endif
